package utils.data

import scala.collection.mutable.ArrayBuffer
import utils.CID

class COptionNode[T <: CNode[T]](node:CNode[T])(implicit tree:CTree[T])
{
    var _node:Option[T] = Some(node.asInstanceOf[T])

    def apply() = _node.get

    def parent:this.type =
    {
        _node = _node flatMap (_.parent)
        this
    }

    def children:Seq[T] =
    {
        _node foreach (n => return n.children)
        Seq.empty
    }

    def firstChild:this.type =
    {
        _node = _node flatMap (_.children.headOption)
        this
    }

    def secondChild:this.type =
    {
        _node = _node flatMap (c => if (c.children.size > 1) c.children.view.tail.headOption else None)
        this
    }

    def preLastChild:this.type =
    {
        _node = _node flatMap (c => if (c.children.size > 1) c.children.view.init.lastOption else None)
        this
    }

    def lastChild:this.type =
    {
        _node = _node flatMap (_.children.lastOption)
        this
    }

    def nChild(index:Int):this.type =
    {
        _node = if (_node.exists(_.children.size > index - 1)) _node map (_.children(index - 1)) else None
        this
    }

    def isChildExist(condition:(T)=>Boolean) = _node exists (_.children exists condition)
    def is(condition:(T)=>Boolean) = _node exists condition
    def handle(action:(T)=>Unit) = _node foreach action
}

class CNode[T <: CNode[T]](implicit tree:CTree[T])
{
    val id = CNode.id.long

    def apply() = new COptionNode[T](this)

    def children = tree children this.asInstanceOf[T]

    def parent = tree parent this.asInstanceOf[T]

    def addNode(node:T):T =
    {
        tree.addNode(node)
        tree.bind(node, this.asInstanceOf[T])
        this.asInstanceOf[T]
    }

    override def hashCode = id.hashCode
    override def equals(that:Any) = that != null && that.isInstanceOf[CNode[T]] && that.asInstanceOf[CNode[T]].id == id
}

object CNode
{
    private val id = new CID()
}

class CTree[T <: CNode[T]]()
{
    private val _nodes = collection.mutable.HashMap[Long, T]()
    private val _parents = collection.mutable.HashMap[Long, Long]()
    private val _children = collection.mutable.HashMap[Long, ArrayBuffer[Long]]()

    def root:T = _nodes.values find (_.parent == None) get

    def nodes:collection.Map[Long, T] = _nodes

    def children(node:T):Seq[T] = _children getOrElse (node.id, ArrayBuffer()) map (id => _nodes(id))

    def parent(node:T):Option[T] = _parents get node.id map (id => _nodes(id))

    def addNode(node:T) =
    {
        _nodes += node.id -> node
        node
    }

    def bind(node:T, parent:T) =
    {
        _parents += node.id -> parent.id
        if (_children contains parent.id unary_!) _children += parent.id -> ArrayBuffer[Long]()
        _children(parent id) += node.id
        node
    }
}

object CTree
{
    def apply[T <: CNode[T]]() = new CTree[T]()
}
